package com.kkwrite.regimen.provider.service.point;

import com.kkwrite.regimen.common.constant.DeletedEnum;
import com.kkwrite.regimen.common.constant.SortTypeEnum;
import com.kkwrite.regimen.common.entity.dto.OutDTO;
import com.kkwrite.regimen.common.entity.dto.point.PageablePointRecordOutDTO;
import com.kkwrite.regimen.common.entity.dto.point.PageablePointRecordOutDTO.OutData;
import com.kkwrite.regimen.provider.domain.RegPointRecordDO;
import com.kkwrite.regimen.provider.repository.RegPointRecordRepository;
import com.kkwrite.regimen.provider.service.share.ShareService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;

/**
 * Created by lisha on 2018/7/21 18:04.
 *
 * @author lisha
 */
@Service("pointService")
public class PointServiceImpl implements PointService {

    @Resource(name = "regPointRecordRepository")
    private RegPointRecordRepository repository;
    @PersistenceContext
    private EntityManager em;
    @Resource(name = "shareService")
    private ShareService shareService;

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OutDTO<PageablePointRecordOutDTO> listRecords(Integer page, Integer size, Integer userId, Integer sortType) {
        Sort.Direction direction = Sort.Direction.DESC;
        if (SortTypeEnum.POSITIVE_SEQUENCE.getValue().equals(sortType)) {
            direction = Sort.Direction.ASC;
        }
        Pageable pageable = new PageRequest(page, size, new Sort(direction, "gmtCreate"));
        RegPointRecordDO exampleDO = new RegPointRecordDO();
        exampleDO.setUserId(userId);
        Page<RegPointRecordDO> pageData = repository.findAll(Example.of(exampleDO), pageable);
        PageablePointRecordOutDTO out = new PageablePointRecordOutDTO();
        out.setTotalPages(pageData.getTotalPages());
        out.setTotalElements(pageData.getTotalElements());
        final List<PageablePointRecordOutDTO.OutData> recordList = out.getRecordList();
        pageData.getContent().forEach((recordDO) -> {
            PageablePointRecordOutDTO.OutData outData = out.createOutData();
            BeanUtils.copyProperties(recordDO, outData);
            outData.setCreateTime(recordDO.getGmtCreate().toLocalDateTime());
            recordList.add(outData);
        });
        return new OutDTO<>("true", null, null, out);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public OutData queryPointTotal(Integer userId) {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Tuple> cq = cb.createTupleQuery();
        Root<RegPointRecordDO> root = cq.from(RegPointRecordDO.class);
        cq.select(cb.tuple(cb.sum(root.get("pointValue").as(Integer.class)).alias("total")))
                .where(cb.equal(root.get("userId").as(Integer.class), userId));
        Tuple tuple = em.createQuery(cq).getSingleResult();
        Long pointTotal = tuple.get("total", Long.class);
        PageablePointRecordOutDTO out = new PageablePointRecordOutDTO();
        OutData outData = out.createOutData();
        outData.setPointTotal(pointTotal);
        return outData;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPointRecord(int userId, int pointValue, String pointSource) {
        newRecord(userId, pointValue, pointSource);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSharePointRecord(int userId, int pointValue, String pointSource, short type, String itemId) {
        if (shareService.isFirstShare(userId, type, itemId)) {
            newRecord(userId, pointValue, pointSource);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void newRecord(int userId, int pointValue, String pointSource) {
        if (pointValue == 0) {
            return;
        }
        Assert.isTrue(userId > 0, "userId must be greater than zero");
        Assert.hasText(pointSource, "pointSource must not be null or empty");
        RegPointRecordDO pointRecord = new RegPointRecordDO();
        pointRecord.setUserId(userId);
        pointRecord.setPointSource(pointSource);
        pointRecord.setPointValue(pointValue);
        pointRecord.setDeleted(DeletedEnum.UN_DELETED.getValue());
        repository.saveAndFlush(pointRecord);
    }

}
